package main

/*
#include <stdlib.h>
*/
import (
	"C"
	"bytes"
	"encoding/binary"
	"fmt"
	"log"
	"math"
	"time"

	"github.com/goburrow/modbus"
)
import "strings"

var handler *modbus.TCPClientHandler

//export Connect
func Connect(ip *C.char, port C.int) C.int {
	log.Printf("Connect ...")
	handler = modbus.NewTCPClientHandler(fmt.Sprintf("%s:%d", C.GoString(ip), int(port)))
	handler.Timeout = 250 * time.Millisecond
	err := handler.Connect()
	if err != nil {
		log.Printf("Connect error: %v", err)
		return -1
	}
	return 0
}

//export Close
func Close() C.int {
	log.Printf("Close ...")
	err := handler.Close()
	if err != nil {
		log.Printf("Close error: %v", err)
		return -1
	}
	return 0
}

//ReadRegister 一般错误返回 -1 连接失败返回 -2
//export ReadRegister
func ReadRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
	var results []byte
	var err error

	handler.SlaveId = byte(slaveId)

	client := modbus.NewClient(handler)

	dt := C.GoString(dataType)

	switch dt {
	case "int16":
		results, err = client.ReadInputRegisters(uint16(address), 1)
	case "int32", "float":
		results, err = client.ReadInputRegisters(uint16(address), 2)
	case "int64", "double":
		results, err = client.ReadInputRegisters(uint16(address), 4)
	}

	if err != nil {
		switch {
		case strings.Contains(err.Error(), "connection"):
			log.Println("Connection error:", err)
			return -3
		case strings.Contains(err.Error(), "timeout"):
			log.Println("Timeout error:", err)
			return -1
		default:
			log.Println("Other error:", err)
			return -1
		}
	}

	var value float64
	switch dt {
	case "int16":
		var intValue int16
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
		value = float64(intValue)
	case "int32":
		var intValue int32
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
		value = float64(intValue)
	case "float":
		var floatValue float32
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
		value = float64(floatValue)
	case "int64":
		var intValue int64
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
		value = float64(intValue)
	case "double":
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
	}

	if err != nil {
		log.Println(err)
		return -1
	}

	*data = C.double(value)
	return 0
}

//ReadHoldingRegister 一般错误返回 -1 连接失败返回 -2
//export ReadHoldingRegister
func ReadHoldingRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
	var results []byte
	var err error

	handler.SlaveId = byte(slaveId)

	client := modbus.NewClient(handler)

	dt := C.GoString(dataType)
	// startTime := time.Now()
	switch dt {
	case "int16":
		results, err = client.ReadHoldingRegisters(uint16(address), 1)
	case "int32", "float":
		results, err = client.ReadHoldingRegisters(uint16(address), 2)
	case "int64", "double":
		results, err = client.ReadHoldingRegisters(uint16(address), 4)
	}
	// endTime := time.Now()

	// // 计算时间差
	// duration := endTime.Sub(startTime)
	// fmt.Printf("Takes: %v\n", duration)
	if err != nil {
		switch {
		case strings.Contains(err.Error(), "connection"):
			log.Println("Connection error:", err)
			return -3
		case strings.Contains(err.Error(), "timeout"):
			log.Println("Timeout error:", err)
			return -1
		default:
			log.Println("Other error:", err)
			return -1
		}
	}

	var value float64
	switch dt {
	case "int16":
		var intValue int16
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
		value = float64(intValue)
	case "int32":
		var intValue int32
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
		value = float64(intValue)
	case "float":
		var floatValue float32
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
		value = float64(floatValue)
	case "int64":
		var intValue int64
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
		value = float64(intValue)
	case "double":
		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
	}

	if err != nil {
		log.Println(err)
		return -1
	}

	*data = C.double(value)
	return 0
}

//export WriteRegister
func WriteRegister(slaveId C.int, address C.int, dataType *C.char, value C.double) C.int {
	var err error

	handler.SlaveId = byte(slaveId)
	client := modbus.NewClient(handler)

	dt := C.GoString(dataType)

	switch dt {
	case "int16":
		var intValue int16 = int16(value)
		_, err = client.WriteSingleRegister(uint16(address), uint16(intValue))
	case "int32":
		var intValue int32 = int32(value)
		results := make([]byte, 4)
		binary.BigEndian.PutUint32(results, uint32(intValue))
		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
	case "float":
		var floatValue float32 = float32(value)
		results := make([]byte, 4)
		binary.BigEndian.PutUint32(results, math.Float32bits(floatValue))
		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
	case "int64":
		var intValue int64 = int64(value)
		results := make([]byte, 8)
		binary.BigEndian.PutUint64(results, uint64(intValue))
		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
	case "double":
		var doubleValue float64 = float64(value)
		results := make([]byte, 8)
		binary.BigEndian.PutUint64(results, math.Float64bits(doubleValue))
		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
	default:
		return -1
	}

	if err != nil {
		switch {
		case strings.Contains(err.Error(), "connection"):
			log.Println("Connection error:", err)
			return -3
		case strings.Contains(err.Error(), "timeout"):
			log.Println("Timeout error:", err)
			return -1
		default:
			log.Println("Other error:", err)
			return -1
		}
	}
	return 0
}

func main() {}
